import Phaser from 'phaser'
import { SceneKeys, TextureKeys } from '~/consts/Keys'
import Laser from '~/objects/Laser'
import RocketMouse from '~/objects/RocketMouse'

export default class GameScene extends Phaser.Scene {
  private background!: Phaser.GameObjects.TileSprite
  private decorations!: Phaser.GameObjects.Image[]
  private obstacles!: Phaser.GameObjects.Container[]
  private coins!: Phaser.Physics.Arcade.StaticGroup
  private scoreLabel!: Phaser.GameObjects.Text
  private score = 0
  private mouse!: RocketMouse

  constructor () {
    super('game')
  }
  init () {
    this.score = 0
  }
  create () {
    const { width, height } = this.scale
    const floorY = height - 55

    // physics world
    this.physics.world.setBounds(
      0, 0, // x, y
      Number.MAX_SAFE_INTEGER, floorY // width, height
    )

    // background & decorations
    this.background = this.add.tileSprite(0, 0, width, height, TextureKeys.Background)
        .setOrigin(0).setScrollFactor(0)

    // decorations should not overlap
    this.decorations = [
      this.add.image(Phaser.Math.Between(900, 1500), 501, TextureKeys.MouseHole),
      this.add.image(Phaser.Math.Between(900, 1300), 200, TextureKeys.Window1),
      this.add.image(Phaser.Math.Between(1600, 2000), 200, TextureKeys.Window2),
      this.add.image(Phaser.Math.Between(2200, 2700), 580, TextureKeys.Bookcase1)
          .setOrigin(0.5, 1),
      this.add.image(Phaser.Math.Between(2900, 3400), 580, TextureKeys.Bookcase2)
        .setOrigin(0.5, 1),
    ]

    // laser
    const laser = new Laser(this, 900, 100)
    this.obstacles = [ this.add.existing(laser) ]

    // coins
    this.coins = this.physics.add.staticGroup()
    this.spawnCoins()

    // mouse
    this.mouse = new RocketMouse(this, width * 0.5, floorY + 22)
    this.add.existing(this.mouse)
    this.cameras.main.startFollow(this.mouse)
        .setBounds(0, 0, Number.MAX_SAFE_INTEGER, height)

    // score
    this.scoreLabel = this.add.text(10, 10, 'Score: 0', {
      fontSize: '24px',
      fontStyle: 'bold',
      color: '#fff',
      shadow: { fill: true, blur: 0, offsetY: 0 },
      padding: { left: 15, right: 15, top: 10, bottom: 10 }
    }).setScrollFactor(0)

    // on kill
    this.physics.add.overlap(
      this.obstacles[0], this.mouse,
      (laser, mouse) => {
        this.mouse.kill()
      }
    )

    // on collect coin
    this.physics.add.overlap(
      this.coins, this.mouse,
      (mouse, obj) => {
        const coin = obj as Phaser.Physics.Arcade.Sprite
        this.coins.killAndHide(coin)
        coin.body.enable = false
        this.score += 10
        this.scoreLabel.text = `Score: ${this.score}`
      }
    )

    // on game over
    this.mouse.once('dead', () => {
      this.scene.run(SceneKeys.GameOver) // 直接在原有画面上绘制场景
      this.input.keyboard.once('keydown-SPACE', () => {
        this.scene.stop(SceneKeys.GameOver)
        this.scene.restart()
        // this.scene.stop(SceneKeys.Game)
        // this.scene.start(SceneKeys.Game)
      })
    })
  }
  update (t: number, dt: number) {
    // scroll the background
    this.background.setTilePosition(this.cameras.main.scrollX)
    // decorations & objects are reused
    this.wrap()
    // teleport backward if bound were passed
    this.teleport()
  }
  private wrap () {
    const leftEdge = this.cameras.main.scrollX
    const rightEdge = leftEdge + this.scale.width

    this.decorations.forEach((obj, index, arr) => {
      const padding = obj.width
      if (obj.x + padding < leftEdge) {
        obj.x = rightEdge + padding + Phaser.Math.Between(0, 800)
        // TODO: 用线段树做碰撞检测?
        const overlap = arr.find(other =>
          2 * Math.abs(obj.x - other.x) <= obj.width + other.width + 20 && other !== obj
        )
        // hide obj if overlap
        obj.visible = !overlap
      }
    })

    this.obstacles.forEach((obj, index, arr) => {
      const body = obj.body as Phaser.Physics.Arcade.StaticBody
      const padding = body.width
      if (obj.x + padding < leftEdge) {        
        obj.x = rightEdge + Phaser.Math.Between(0, 1000)
        obj.y = Phaser.Math.Between(0, 300)
        body.position.x = obj.x + body.offset.x
        body.position.y = obj.y + body.offset.y
      }
    })
  }
  private spawnCoins () {
    // hide all
    this.coins.children.each(child => {
      const coin = child as Phaser.Physics.Arcade.Sprite
      this.coins.killAndHide(coin)
      coin.body.enable = false
    })

    const leftEdge = this.cameras.main.scrollX
    const rightEdge = leftEdge + this.scale.width
    const count = Phaser.Math.Between(1, 20)
    let x = rightEdge + 100
    for (let i = 0; i < count; ++i) {
      const coin = this.coins.get(
        x,
        Phaser.Math.Between(100, this.scale.height - 100),
        TextureKeys.Coin
      ) as Phaser.Physics.Arcade.Sprite
      coin.setVisible(true).setActive(true)

      const body = coin.body as Phaser.Physics.Arcade.StaticBody
      body.setCircle(body.width * 0.5)
      body.enable = true
      body.updateFromGameObject()
      x += coin.width * 1.5
    }
  }
  private teleport () {
    const scrollX = this.cameras.main.scrollX
    const maxX = 7 * 340 // 340 = background texture.width
    if (scrollX > maxX) {
      this.mouse.x -= maxX
      this.decorations.forEach(obj => obj.x -= maxX)
      const teleportStatic = obj => {
        obj.x -= maxX
        const body = obj.body as Phaser.Physics.Arcade.StaticBody
        body.x -= maxX
      }
      this.obstacles.forEach(teleportStatic)
      this.spawnCoins()
      this.coins.children.each(child => {
        const coin = child as Phaser.Physics.Arcade.Sprite
        if (coin.active) {
          teleportStatic(coin)
          coin.body.updateFromGameObject()
        }
      })
    }  
  }
}